本课时我将为你介绍发生死锁必须满足哪 4 个条件。
要想发生死锁有 4 个缺一不可的必要条件,我们一个个来看:
下面我们回到上一课时中所写的必然死锁的例子中,看看它是否一一满足了这 4 个条件,案例代码如下所示:
/**
* 描述: 必定死锁的情况
*/
public class MustDeadLock implements Runnable {
public int flag;
static Object o1 = new Object();
static Object o2 = new Object();
public void run() {
System.out.println("线程"+Thread.currentThread().getName() + "的flag为" + flag);
if (flag == 1) {
synchronized (o1) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o2) {
System.out.println("线程1获得了两把锁");
}
}
}
if (flag == 2) {
synchronized (o2) {
try {
Thread.sleep(500);
} catch (Exception e) {
e.printStackTrace();
}
synchronized (o1) {
System.out.println("线程2获得了两把锁");
}
}
}
}
public static void main(String[] argv) {
MustDeadLock r1 = new MustDeadLock();
MustDeadLock r2 = new MustDeadLock();
r1.flag = 1;
r2.flag = 2;
Thread t1 = new Thread(r1, "t1");
Thread t2 = new Thread(r2, "t2");
t1.start();
t2.start();
}
}
这个代码的具体分析和执行结果,我们在上一课时中已经介绍过了,这里不重复讲解,下面我们把重点放在对这 4 个必要条件的分析上。
我们先来看一下第 1 个互斥条件,很显然,我们使用的是 synchronized 互斥锁,它的锁对象 o1、o2 只能同时被一个线程所获得,所以是满足互斥条件的。
第 2 个是请求与保持条件,可以看到,同样是满足的。比如,线程 1 在获得 o1 这把锁之后想去尝试获取 o2 这把锁 ,这时它被阻塞了,但是它并不会自动去释放 o1 这把锁,而是对已获得的资源保持不放。
第 3 个是不剥夺条件,在我们这个代码程序中,JVM 并不会主动把某一个线程所持有的锁剥夺,所以也满足不剥夺条件。
第 4 个是循环等待条件,可以看到在我们的例子中,这两个线程都想获取对方已持有的资源,也就是说线程 1 持有 o1 去等待 o2,而线程 2 则是持有 o2 去等待 o1,这是一个环路,此时就形成了一个循环等待。
可以看出,在我们的例子中确实满足这 4 个必要条件,今后我们就可以从这 4 个发生死锁的必要条件出发,来解决死锁的问题,只要破坏任意一个条件就可以消除死锁,这也是我们后面要讲的解决死锁策略中重点要考虑的内容。
以上就是本课时的内容,我们总结一下,在本课时主要介绍了要想发生死锁,必须满足的 4 个条件,分别是互斥条件、请求与保持条件、不剥夺条件和循环等待条件;同时还分析了在上一课时中必然发生死锁的例子,可以看到,在这个例子中确实满足了这 4 个条件。